package slap.mvc;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jodd.format.Printf;

import slap.Slap;
import slap.app.Application;
import slap.exception.NotFoundException;
import slap.http.Request;
import slap.http.Response;
import slap.log.ACLogger;
import slap.mvc.result.Forbidden;
import slap.mvc.result.NotFound;
import slap.mvc.result.Result;
import slap.mvc.result.Error;
import slap.paradigm.Procedure.Procedure2;
import slap.paradigm.ProcedureV;

public class ActionInvoker {
	
	protected static final Logger logger = LoggerFactory.getLogger(ActionInvoker.class);
	
	
	protected static void invokeWithResultCacheable(Procedure2<Request, Response> proc, Request request, Response response){
		try{
			proc.invoke(request, response);
		} catch (Exception ex) {
			Result actionResult = null;
			if(ex instanceof InvocationTargetException){
				InvocationTargetException ite = (InvocationTargetException) ex;
				if(ite.getTargetException() instanceof Result){
					actionResult = (Result) ite.getTargetException();
				}
			}else if(ex instanceof Result) {
                actionResult = (Result) ex;
            }
			
			if(actionResult != null){
				final Result result = actionResult;
				invokeWithResultCacheable(new Procedure2<Request, Response>(){
					@Override
					public void invoke(Request request, Response response) {
						result.apply(request, response);
					}
				},request, response);
			}else{
            	ex.printStackTrace();
            }
			
		}
	}
	
	public static void invoke(final Request request, final Response response){
		invokeWithResultCacheable(new Procedure2<Request, Response>(){
			@Override
			public void invoke(Request t1, Response t2) throws Exception {
				resolveAction(request, response);
			}
			
		}, request, response);
	}
	
	protected static Application resolveApplication(Request request){
		Application app = Slap.applications.get(request.domain());
		if(app == null){
			if(Slap.defaultApplication == null){
				throw new Error("Can not resolve application : No default app specified.");
			}else{
				app = Slap.defaultApplication;
			}
		}
		return app;
	}
	
	protected static void resolveAction(Request request, Response response) throws Exception{
		Application app = resolveApplication(request);
		if(app == null){
			logger.info("[{}] cannot be resolved, no app mapped.", request.uri());
			throw new NotFound(request.uri());
		}
		Action action = null;
		action = app.directRouter.route(request);
		if(action == null){
			action = app.wildcardRouter.route(request);
		}
		if(action == null){
			action = app.regexRouter.route(request);
		}
		if(action == null){
			logger.info("[{}] cannot be resolved, no action mapped.", request.uri());
			throw new NotFound(request.uri());
		}
		action.excute(request, response);
	}
	
}
